import { Callout } from "nextra/components";

# React Server Components

Puck provides support for [React Server Components](https://react.dev/reference/react/use-server#use-server) (RSC), but the interactive-nature of Puck requires special consideration.

## Environments

### Server

Puck supports the server environment for the following APIs:

- The [`<Render>`](/docs/api-reference/components/render) component, for rendering pages produced by Puck
- The [`resolveAllData`](/docs/api-reference/functions/resolve-all-data) lib, for running all [data resolvers](/docs/integrating-puck/dynamic-props)

These APIs can be used in an RSC environment, but in order to do so the Puck config that they reference must be RSC-friendly.

This can be done by either avoiding client-only code (React `useState`, etc), or split out client components with the `"use client";` directive.

### Client

All other Puck APIs, including the core `<Puck>` component, cannot run in an RSC environment due to their high-degree of interactivity.

As these APIs render on the client, the Puck config provided must be safe for client-use, avoiding any server-specific logic.

## Implementations

Since the Puck config can be referenced on the client or the server, we need to consider how to satisfy both environments.

There are three approaches to this:

1. Avoid using any client-specific functionality (like React `useState`) in your components
2. Mark your components up with the `"use client";` directive if you need client-specific functionality
3. Create separate configs for client and server rendering

### 1. Avoid client-specific code

Avoiding client-specific code is the easiest way to support RSC across both environments, but may not be realistic for all users. This normally means avoiding React hooks like `useState` or `useContext`.

<Callout type="info">If you're using the legacy [`<DropZone>` component](/docs/api-reference/components/drop-zone), you will may encounter issues with server components. We recommend migrating to the [`slot` field](/docs/api-reference/fields/slot) which provides native server component support. Alternatively, see [our previous server components guide](https://puckeditor.com/v/0.18.3/docs/integrating-puck/server-components) for information on handling DropZones.</Callout>

### 2. Marking up components with `"use client";`

Many modern component libraries will require some degree of client-side behaviour. For these cases, you'll need to mark them up with the `"use client";` directive.

To achieve this, you must import each of those component from a separate file:

```tsx copy showLineNumbers filename="puck.config.tsx"
import type { Config } from "@measured/puck";
import type { HeadingBlockProps } from "./components/HeadingBlock";
import HeadingBlock from "./components/HeadingBlock";

type Props = {
  HeadingBlock: HeadingBlockProps;
};

export const config: Config<Props> = {
  components: {
    HeadingBlock: {
      fields: {
        title: { type: "text" },
      },
      defaultProps: {
        title: "Heading",
      },
      // You must call the component, rather than passing it in directly. This will change in the future.
      render: ({ title }) => <HeadingBlock title={title} />,
    },
  },
};
```

And add the `"use client";` directive to the top of each component file:

```tsx copy showLineNumbers filename="components/HeadingBlock.tsx" {1}
"use client";

import { useState } from "react";

export type HeadingBlockProps = {
  title: string;
};

export default ({ title }: { title: string }) => {
  useState(); // useState fails on the server

  return (
    <div style={{ padding: 64 }}>
      <h1>{title}</h1>
    </div>
  );
};
```

This config can now be rendered inside an RSC component, such as a Next.js app router page:

```tsx copy showLineNumbers filename="app/page.tsx"
import { config } from "../puck.config.tsx";

export default async function Page() {
  const data = await getData(); // Some server function

  const resolvedData = await resolveAllData(data, config); // Optional call to resolveAllData, if this needs to run server-side

  return <Render data={resolvedData} config={config} />;
}
```

### 3. Creating separate configs

Alternatively, consider entirely separate configs for the `<Puck>` and `<Render>` components. This approach can enable you to have different rendering behavior for a component for when it renders on the client or the server.

Create a shared config type:

```tsx copy showLineNumbers filename="puck-types.ts"
import type { Config } from "@measured/puck";

type Props = {
  HeadingBlock: {
    title: string;
  };
};

export type UserConfig = Config<Props>;
```

Define a client component config for use within the `<Puck>` component:

```tsx copy showLineNumbers filename="puck.config.client.tsx"
import type { UserConfig } from "./puck-types.ts";

export const config: UserConfig = {
  components: {
    HeadingBlock: {
      fields: {
        title: { type: "text" },
      },
      defaultProps: {
        title: "Heading",
      },
      render: ({ title }) => {
        useState(); // useState fails on the server

        return (
          <div style={{ padding: 64 }}>
            <h1>{title}</h1>
          </div>
        );
      },
    },
  },
};
```

Define a server config using the shared types for use within the `<Render>` component, excluding fields as they are unnecessary in this environment:

```tsx copy showLineNumbers filename="puck.config.server.tsx"
import type { UserConfig } from "./puck-types.ts";

export const config: UserConfig = {
  components: {
    HeadingBlock: {
      render: ({ title }) => {
        return (
          <div style={{ padding: 64 }}>
            <h1>{title}</h1>
          </div>
        );
      },
    },
  },
};
```

Render the appropriate config depending on the environment. Here's a Next.js app router example of a server render:

```tsx copy showLineNumbers filename="app/page.tsx"
import { config } from "../puck.config.server.tsx";

export default async function Page() {
  const data = await getData(); // Some server function

  return <Render data={data} config={config} />;
}
```
